using System;
using System.Collections.Generic;
using opt.Analysis.Graph;
using opt.Analysis.Graph.ControlFlowGraph;
using CLELCore.Assembly.CLELInstructions;

namespace opt.Analysis.Set
{
	public class ReachingDefinitionsVisit : IGraphVisit<CFGArcInfo,CFGNodeInfo>
	{
		private bool changed;
		private List<BitSet> gen;
		private List<BitSet> kill;
		private List<BitSet> in_set;
		private List<BitSet> out_set;

		public bool Changed
		{
			get
			{
				return changed;
			}
			set
			{
				changed = value;
			}
		}

		public ReachingDefinitionsVisit(List<BitSet> gen,List<BitSet> kill,List<BitSet> in_set,List<BitSet> out_set)
		{
			if(gen == null)
				throw new ArgumentException("Gen cannot be null.");
			if(kill == null)
				throw new ArgumentException("Kill cannot be null.");
			if(in_set == null)
				throw new ArgumentException("In set cannot be null.");
			if(out_set == null)
				throw new ArgumentException("Out set cannot be null.");
			changed = false;
			this.gen = gen;
			this.kill = kill;
			this.in_set = in_set;
			this.out_set = out_set;
		}
		
		public void Visit(GraphNode<CFGArcInfo,CFGNodeInfo> node)
		{
			if(node == null)
				throw new ArgumentException("Control Flow Graph node cannot be null.");
			List<CLELInstruction> code = node.Data.Code;
			int start_instruction = node.Data.StartInstructionIndex;
			int code_count = code.Count;
			int back_count = node.BackCount;
			GraphNode<CFGArcInfo,CFGNodeInfo> back_node;
			int back_code_count,back_instruction_index;
			int i,j,orig_instruction_index;
			BitSet temp,temp2;
			for(i = 0;i < code_count;i++)
			{
				orig_instruction_index = start_instruction+i;
				temp = new BitSet();
				temp2 = new BitSet();
				if(i == 0)
				{
					//use back arcs to get previous instructions
					for(j = 0;j < back_count;j++)
					{
						back_node = node.GetBackGraphArc(j).Next;
						back_code_count = back_node.Data.CodeCount;
						back_instruction_index = back_node.Data.StartInstructionIndex;
						temp = temp.Union(out_set[back_instruction_index+back_code_count-1]);
					}
				}
				else
				{
					temp = temp.Union(out_set[orig_instruction_index-1]);
				}
				if(!temp.Equals(in_set[orig_instruction_index]))
					changed = true;
				in_set[orig_instruction_index] = temp;

				temp2 = in_set[orig_instruction_index].Remove(kill[orig_instruction_index]);
				temp2 = temp2.Union(gen[orig_instruction_index]);
				if(!temp2.Equals(out_set[orig_instruction_index]))
					changed = true;
				out_set[orig_instruction_index] = temp2;
			}
		}
	}
}
